/*
 * Copyright 2018 Rami Jemli
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ramijemli.percentagechartview;

import com.ramijemli.percentagechartview.annotation.ChartMode;
import com.ramijemli.percentagechartview.annotation.GradientTypes;
import com.ramijemli.percentagechartview.annotation.ProgressBarStyle;
import com.ramijemli.percentagechartview.annotation.ProgressOrientation;
import com.ramijemli.percentagechartview.callback.AdaptiveColorProvider;
import com.ramijemli.percentagechartview.callback.OnProgressChangeListener;
import com.ramijemli.percentagechartview.callback.ProgressTextFormatter;
import com.ramijemli.percentagechartview.renderer.BaseModeRenderer;
import com.ramijemli.percentagechartview.renderer.FillModeRenderer;
import com.ramijemli.percentagechartview.renderer.OffsetEnabledMode;
import com.ramijemli.percentagechartview.renderer.OrientationBasedMode;
import com.ramijemli.percentagechartview.renderer.PieModeRenderer;
import com.ramijemli.percentagechartview.renderer.RingModeRenderer;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.app.Context;

import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.GRADIENT_LINEAR;
import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.GRADIENT_SWEEP;
import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.MODE_FILL;
import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.MODE_PIE;
import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.MODE_RING;
import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.ORIENTATION_CLOCKWISE;
import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.ORIENTATION_COUNTERCLOCKWISE;

/**
 * Percentage chart view
 */
public class PercentageChartView extends Component implements IPercentageChartView {
    /**
     * * The constant PercentageChartView_pcv_mode
     */
    public static final String PERCENTAGECHARTVIEW_PCV_MODE = "pcv_mode";
    /**
     * The constant Renderer
     */
    private BaseModeRenderer renderer;

    /**
     * The constant Mode
     */
    @ChartMode
    private int mode;

    /**
     * The constant On progress change listener
     */
    private OnProgressChangeListener onProgressChangeListener;

    /**
     * Percentage chart view
     *
     * @param context context
     */
    public PercentageChartView(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * Percentage chart view
     *
     * @param context context
     * @param attrs   attrs
     */
    public PercentageChartView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * Percentage chart view
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr def style attr
     */
    public PercentageChartView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }


    /**
     * Init *
     *
     * @param context context
     * @param attrs   attrs
     */
    private void init(Context context, AttrSet attrs) {
        // CHART MODE (DEFAULT PIE MODE)
        mode = AttrUtils.getIntFromAttr(attrs, PERCENTAGECHARTVIEW_PCV_MODE, MODE_PIE);
        switch (mode) {
            case MODE_RING:
                renderer = new RingModeRenderer(this, attrs);
                break;
            case MODE_FILL:
                renderer = new FillModeRenderer(this, attrs);
                break;

            default:
            case MODE_PIE:
                renderer = new PieModeRenderer(this, attrs);
                break;
        }
        setLayoutRefreshedListener(this::onRefreshed);
        addDrawTask(this::onDraw);
    }

    /**
     * On draw *
     *
     * @param component component
     * @param canvas    canvas
     */
    private void onDraw(Component component, Canvas canvas) {
        renderer.draw(canvas);
    }

    /**
     * On refreshed *
     *
     * @param component component
     */
    private void onRefreshed(Component component) {
        int width = getWidth();
        int height = getHeight();
        if (renderer != null) {
            renderer.measure(width, height, getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
        }
    }

    /**
     * Gets the text font.
     *
     * @return the text typeface
     */
    public Font.Builder getTypeface() {
        return renderer.getTypeface();
    }

    /**
     * Sets the text font.
     *
     * @param typeface the text font as a Typeface instance
     */
    @SuppressWarnings("ConstantConditions")
    public void setTypeface(Font.Builder typeface) {
        typeface(typeface);
        postInvalidate();
    }

    /**
     * Sets the text font.
     *
     * @param typeface the text font as a Typeface instance
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given typeface is null.
     */
    @SuppressWarnings("ConstantConditions")
    public PercentageChartView typeface(Font.Builder typeface) {
        if (typeface == null) {
            throw new IllegalArgumentException("Text TypeFace cannot be null");
        }
        renderer.setTypeface(typeface);
        return this;
    }

    /**
     * Get view context context
     *
     * @return the context
     */
    @Override
    public Context getViewContext() {
        return getContext();
    }

    /**
     * Post invalidate
     */
    @Override
    public void postInvalidate() {
        invalidate();
    }

    /**
     * Post invalidate on animation
     */
    @Override
    public void postInvalidateOnAnimation() {
        invalidate();
    }

    /**
     * Is in edit mode boolean
     *
     * @return the boolean
     */
    @Override
    public boolean isInEditMode() {
        return false;
    }

    /**
     * On progress updated *
     *
     * @param progress progress
     */
    @Override
    public void onProgressUpdated(float progress) {
        if (onProgressChangeListener != null) {
            onProgressChangeListener.onProgressChanged(progress);
        }
    }

    /**
     * Gets the percentage chart view mode.
     *
     * @return the percentage chart view mode
     */
    @ChartMode
    public int getMode() {
        return mode;
    }

    /**
     * Gets the current drawing orientation.
     *
     * @return the current drawing orientation
     */
    @ProgressOrientation
    public int getOrientation() {
        if (!(renderer instanceof OrientationBasedMode)) {
            return BaseModeRenderer.INVALID_ORIENTATION;
        }
        return ((OrientationBasedMode) renderer).getOrientation();
    }

    /**
     * Sets the circular drawing direction. Default orientation is ORIENTATION_CLOCKWISE.
     *
     * @param orientation non-negative orientation constant.
     */
    public void setOrientation(@ProgressOrientation int orientation) {
        orientation(orientation);
        postInvalidate();
    }

    /**
     * Gets the current circular drawing's start angle.
     *
     * @return the current circular drawing's start angle
     */
    public float getStartAngle() {
        return renderer.getStartAngle();
    }

    /**
     * Sets the current circular drawing's start angle in degrees. Default start angle is0.
     *
     * @param startAngle A positive start angle value that is less or equal to 360.
     */
    public void setStartAngle(float startAngle) {
        startAngle(startAngle);
        postInvalidate();
    }

    /**
     * Gets whether drawing background has been enabled.
     *
     * @return whether drawing background has been enabled
     */
    public boolean isDrawBackgroundEnabled() {
        return renderer.isDrawBackgroundEnabled();
    }

    /**
     * Sets whether background should be drawn.
     *
     * @param enabled True if background have to be drawn, false otherwise.
     */
    public void setDrawBackgroundEnabled(boolean enabled) {
        drawBackgroundEnabled(enabled);
        postInvalidate();
    }

    /**
     * Gets the circular background color for this view.
     *
     * @return the color of the circular background
     */
    public int getBackgroundColor() {
        return renderer.getBackgroundColor();
    }

    /**
     * Sets the circular background color for this view.
     *
     * @param color the color of the circular background
     */
    public void setBackgroundColor(int color) {
        backgroundColor(color);
        postInvalidate();
    }

    /**
     * Gets the current progress.
     *
     * @return the current progress @FloatRange(from = 0f, to = 100f)
     */
    public float getProgress() {
        return renderer.getProgress();
    }

    /**
     * Sets a new progress value. Passing true in animate will cause an animated progress update.
     *
     * @param progress New progress float value to set.
     * @param animate  Animation boolean value to set whether to animate progress change or not.
     * @throws IllegalArgumentException if the given progress is negative, or, less or equal to 100.
     */
    public void setProgress(float progress, boolean animate) {
        if (progress < 0 || progress > 100) {
            throw new IllegalArgumentException("Progress value must be positive and less or equal to 100.");
        }

        renderer.setProgress(progress, animate);
    }

    /**
     * Gets the progress/progress bar color for this view.
     *
     * @return the progress/progress bar color.
     */
    public int getProgressColor() {
        return renderer.getProgressColor();
    }

    /**
     * Sets the progress/progress bar color for this view.
     *
     * @param color the color of the progress/progress bar
     */
    public void setProgressColor(int color) {
        progressColor(color);
        postInvalidate();
    }


    /**
     * Gets progress gradient type.
     *
     * @return Gets progress gradient type.
     */
    @GradientTypes
    public int getGradientType() {
        return renderer.getGradientType();
    }


    /**
     * Sets progress gradient colors.
     *
     * @param type      The gradient type which is a GradientTypes constant
     * @param colors    The colors to be distributed.                  There must be at least 2 colors in the array.
     * @param positions May be NULL. The relative position of                  each corresponding color in the colors array, beginning                  with 0 and ending with 1.0. If the values are not                  monotonic, the drawing may produce unexpected results.                  If positions is NULL, then the colors are automatically                  spaced evenly.
     * @param angle     Defines the direction for linear gradient type.
     */
    public void setGradientColors(@GradientTypes int type, Color[] colors, float[] positions, float angle) {
        gradientColors(type, colors, positions, angle);
        postInvalidate();
    }

    /**
     * Gets the duration of the progress change's animation.
     *
     * @return the duration of the progress change's animation
     */
    public int getAnimationDuration() {
        return renderer.getAnimationDuration();
    }

    /**
     * Sets the duration of the progress change's animation.
     *
     * @param duration non-negative duration value.
     */
    public void setAnimationDuration(int duration) {
        animationDuration(duration);
    }

    /**
     * Gets the interpolator of the progress change's animation.
     *
     * @return the interpolator of the progress change's animation
     */
    public int getAnimationInterpolator() {
        return renderer.getAnimationInterpolator();
    }

    /**
     * Sets the interpolator of the progress change's animation.
     *
     * @param interpolator TimeInterpolator instance.
     */
    @SuppressWarnings("ConstantConditions")
    public void setAnimationInterpolator(int interpolator) {
        animationInterpolator(interpolator);
    }

    /**
     * Gets the text color.
     *
     * @return the text color
     */
    public int getTextColor() {
        return renderer.getTextColor();
    }

    /**
     * Sets the text color for this view.
     *
     * @param color the text color
     */
    public void setTextColor(int color) {
        textColor(color);
        postInvalidate();
    }

    /**
     * Gets the text size.
     *
     * @return the text size
     */
    public float getTextSize() {
        return renderer.getTextSize();
    }

    /**
     * Sets the text size.
     *
     * @param size the text size
     */
    public void setTextSize(float size) {
        textSize(size);
        postInvalidate();
    }

    /**
     * Gets the text style.
     *
     * @return the text style
     */
    public int getTextStyle() {
        return renderer.getTextStyle();
    }

    /**
     * Sets the text style.
     *
     * @param style the text style.
     */
    public void setTextStyle(int style) {
        textStyle(style);
        postInvalidate();
    }

    /**
     * Gets the text shadow color.
     *
     * @return the text shadow color
     */
    public int getTextShadowColor() {
        return renderer.getTextShadowColor();
    }

    /**
     * Gets the text shadow radius.
     *
     * @return the text shadow radius
     */
    public float getTextShadowRadius() {
        return renderer.getTextShadowRadius();
    }

    /**
     * Gets the text shadow y-axis distance.
     *
     * @return the text shadow y-axis distance
     */
    public float getTextShadowDistY() {
        return renderer.getTextShadowDistY();
    }

    /**
     * Gets the text shadow x-axis distance.
     *
     * @return the text shadow x-axis distance
     */
    public float getTextShadowDistX() {
        return renderer.getTextShadowDistX();
    }

    /**
     * Sets the text shadow. Passing zeros will remove the shadow.
     *
     * @param shadowColor  text shadow color value.
     * @param shadowRadius text shadow radius.
     * @param shadowDistX  text shadow y-axis distance.
     * @param shadowDistY  text shadow x-axis distance.
     */
    public void setTextShadow(int shadowColor, float shadowRadius, float shadowDistX, float shadowDistY) {
        textShadow(shadowColor, shadowRadius, shadowDistX, shadowDistY);
        postInvalidate();
    }

    /**
     * Gets the offset of the circular background.
     *
     * @return the offset of the circular background.-1 if chart mode is not set to pie.
     */
    public float getBackgroundOffset() {
        if (!(renderer instanceof OffsetEnabledMode)) {
            return -1;
        }
        return ((OffsetEnabledMode) renderer).getBackgroundOffset();
    }

    /**
     * Sets the offset of the circular background. Works only if chart mode is set to pie.
     *
     * @param offset A positive offset value.
     */
    public void setBackgroundOffset(int offset) {
        backgroundOffset(offset);
        postInvalidate();
    }

    /**
     * Gets whether drawing the background bar has been enabled.
     *
     * @return whether drawing the background bar has been enabled
     */
    public boolean isDrawBackgroundBarEnabled() {
        if (!(renderer instanceof RingModeRenderer)) {
            return false;
        }
        return ((RingModeRenderer) renderer).isDrawBackgroundBarEnabled();
    }

    /**
     * Sets whether background bar should be drawn.
     *
     * @param enabled True if background bar have to be drawn, false otherwise.
     */
    public void setDrawBackgroundBarEnabled(boolean enabled) {
        drawBackgroundBarEnabled(enabled);
        postInvalidate();
    }

    /**
     * Gets the background bar color.
     *
     * @return the background bar color. -1 if chart mode is not set to ring.
     */
    public int getBackgroundBarColor() {
        if (!(renderer instanceof RingModeRenderer)) {
            return -1;
        }
        return ((RingModeRenderer) renderer).getBackgroundBarColor();
    }

    /**
     * Sets the background bar color.
     *
     * @param color the background bar color
     */
    public void setBackgroundBarColor(int color) {
        backgroundBarColor(color);
        postInvalidate();
    }

    /**
     * Gets the background bar thickness in pixels.
     *
     * @return the background bar thickness in pixels. -1 if chart mode is not set to ring.
     */
    public float getBackgroundBarThickness() {
        if (!(renderer instanceof RingModeRenderer)) {
            return -1;
        }
        return ((RingModeRenderer) renderer).getBackgroundBarThickness();
    }

    /**
     * Sets the background bar thickness in pixels. Works only if chart mode is set to ring.
     *
     * @param thickness non-negative thickness value in pixels.
     */
    public void setBackgroundBarThickness(float thickness) {
        backgroundBarThickness(thickness);
        postInvalidate();
    }

    /**
     * Gets the progress bar thickness in pixels.
     *
     * @return the progress bar thickness in pixels. -1 if chart mode is not set to ring.
     */
    public float getProgressBarThickness() {
        if (!(renderer instanceof RingModeRenderer)) {
            return -1;
        }
        return ((RingModeRenderer) renderer).getProgressBarThickness();
    }

    /**
     * Sets the progress bar thickness in pixels. Works only if chart mode is set to ring.
     *
     * @param thickness non-negative thickness value in pixels.
     */
    public void setProgressBarThickness(float thickness) {
        progressBarThickness(thickness);
        postInvalidate();
    }

    /**
     * Gets the progress bar stroke style.
     *
     * @return the progress bar stroke style. -1 if chart mode is not set to ring.
     */
    public int getProgressBarStyle() {
        if (!(renderer instanceof RingModeRenderer)) {
            return -1;
        }
        return ((RingModeRenderer) renderer).getProgressBarStyle();
    }

    /**
     * Sets the progress bar stroke style. Works only if chart mode is set to ring.
     *
     * @param style Progress bar stroke style as a ProgressStyle constant.
     */
    public void setProgressBarStyle(@ProgressBarStyle int style) {
        progressBarStyle(style);
        postInvalidate();
    }

    /**
     * Sets the circular drawing direction. Default orientation is ORIENTATION_CLOCKWISE.
     *
     * @param orientation non-negative orientation constant.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given orientation is not a ProgressOrientation constant or not supported by the current used chart mode.
     */
    public PercentageChartView orientation(@ProgressOrientation int orientation) {
        if (orientation != ORIENTATION_CLOCKWISE && orientation != ORIENTATION_COUNTERCLOCKWISE) {
            throw new IllegalArgumentException("Orientation must be a ProgressOrientation constant.");
        }

        try {
            ((OrientationBasedMode) renderer).setOrientation(orientation);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Orientation is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Sets the current circular drawing's start angle in degrees. Default start angle is0.
     *
     * @param startAngle A positive start angle value that is less or equal to 360.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given start angle is not positive, or, less or equal to 360.
     */
    public PercentageChartView startAngle(float startAngle) {
        if (startAngle < 0 || startAngle > 360) {
            throw new IllegalArgumentException("Start angle value must be positive and less or equal to 360.");
        }
        this.renderer.setStartAngle(startAngle);
        return this;
    }

    /**
     * Sets whether background should be drawn.
     *
     * @param enabled True if background have to be drawn, false otherwise.
     * @return the percentage chart view
     */
    public PercentageChartView drawBackgroundEnabled(boolean enabled) {
        this.renderer.setDrawBackgroundEnabled(enabled);
        return this;
    }

    /**
     * Sets the circular background color for this view.
     *
     * @param color the color of the circular background
     * @return the percentage chart view
     */
    public PercentageChartView backgroundColor(int color) {
        this.renderer.setBackgroundColor(color);
        return this;
    }

    /**
     * Sets the progress/progress bar color for this view.
     *
     * @param color the color of the progress/progress bar
     * @return the percentage chart view
     */
    public PercentageChartView progressColor(int color) {
        this.renderer.setProgressColor(color);
        return this;
    }

    /**
     * Sets progress gradient colors.
     *
     * @param type      The gradient type which is a GradientTypes constant
     * @param colors    The colors to be distributed.                  There must be at least 2 colors in the array.
     * @param positions May be NULL. The relative position of                  each corresponding color in the colors array, beginning                  with 0 and ending with 1.0. If the values are not                  monotonic, the drawing may produce unexpected results.                  If positions is NULL, then the colors are automatically                  spaced evenly.
     * @param angle     Defines the direction for linear gradient type.
     * @return the percentage chart view
     * @throws IllegalArgumentException If type is not a GradientTypes constant and if colors array is null
     */
    public PercentageChartView gradientColors(@GradientTypes int type, Color[] colors, float[] positions, float angle) {
        if (type < GRADIENT_LINEAR || type > GRADIENT_SWEEP) {
            throw new IllegalArgumentException("Invalid value for progress gradient type.");
        } else if (colors == null) {
            throw new IllegalArgumentException("Gradient colors int array cannot be null.");
        }

        this.renderer.setGradientColors(type, colors, positions, angle);
        return this;
    }

    /**
     * Sets the duration of the progress change's animation.
     *
     * @param duration non-negative duration value.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given duration is less than 50.
     */
    public PercentageChartView animationDuration(int duration) {
        if (duration < 50) {
            throw new IllegalArgumentException("Duration must be equal or greater than 50.");
        }
        renderer.setAnimationDuration(duration);
        return this;
    }

    /**
     * Sets the interpolator of the progress change's animation.
     *
     * @param interpolator TimeInterpolator instance.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given TimeInterpolator instance is null.
     */
    @SuppressWarnings("ConstantConditions")
    public PercentageChartView animationInterpolator(int interpolator) {
        renderer.setAnimationInterpolator(interpolator);
        return this;
    }

    /**
     * Sets the text color for this view.
     *
     * @param color the text color
     * @return the percentage chart view
     */
    public PercentageChartView textColor(int color) {
        renderer.setTextColor(color);
        return this;
    }

    /**
     * Sets the text size.
     *
     * @param size the text size
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given text size is zero or a negative value.
     */
    public PercentageChartView textSize(float size) {
        if (size <= 0) {
            throw new IllegalArgumentException("Text size must be a nonzero positive value.");
        }
        renderer.setTextSize(size);
        return this;
    }

    /**
     * Sets the text style.
     *
     * @param style the text style.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given text style is not a valid TextStyle constant.
     */
    public PercentageChartView textStyle(int style) {
        if (style < 0 || style > 3) {
            throw new IllegalArgumentException("Text style must be a valid TextStyle constant.");
        }
        renderer.setTextStyle(style);
        return this;
    }

    /**
     * Sets the text shadow. Passing zeros will remove the shadow.
     *
     * @param shadowColor  text shadow color value.
     * @param shadowRadius text shadow radius.
     * @param shadowDistX  text shadow y-axis distance.
     * @param shadowDistY  text shadow x-axis distance.
     * @return the percentage chart view
     */
    public PercentageChartView textShadow(int shadowColor, float shadowRadius, float shadowDistX, float shadowDistY) {
        renderer.setTextShadow(shadowColor, shadowRadius, shadowDistX, shadowDistY);
        return this;
    }

    /**
     * Sets the offset of the circular background. Works only if chart mode is set to pie.
     *
     * @param offset A positive offset value.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given offset is a negative value, or, not supported by the current used chart mode.
     */
    public PercentageChartView backgroundOffset(int offset) {
        if (offset < 0) {
            throw new IllegalArgumentException("Background offset must be a positive value.");
        }

        try {
            ((OffsetEnabledMode) renderer).setBackgroundOffset(offset);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Background offset is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Sets whether background bar should be drawn.
     *
     * @param enabled True if background bar have to be drawn, false otherwise.
     * @return the percentage chart view
     * @throws IllegalArgumentException if background bar's drawing state is not supported by the current used chart mode.
     */
    public PercentageChartView drawBackgroundBarEnabled(boolean enabled) {
        try {
            ((RingModeRenderer) renderer).setDrawBackgroundBarEnabled(enabled);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Background bar's drawing state is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Sets the background bar color.
     *
     * @param color the background bar color
     * @return the percentage chart view
     * @throws IllegalArgumentException if background bar color is not supported by the current used chart mode.
     */
    public PercentageChartView backgroundBarColor(int color) {
        try {
            ((RingModeRenderer) renderer).setBackgroundBarColor(color);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Background bar color is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Sets the background bar thickness in pixels. Works only if chart mode is set to ring.
     *
     * @param thickness non-negative thickness value in pixels.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given value is negative, or, background bar thickness is not supported by the current used chart mode.
     */
    public PercentageChartView backgroundBarThickness(float thickness) {
        if (thickness < 0) {
            throw new IllegalArgumentException("Background bar thickness must be a positive value.");
        }

        try {
            ((RingModeRenderer) renderer).setBackgroundBarThickness(thickness);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Background bar thickness is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Sets the progress bar thickness in pixels. Works only if chart mode is set to ring.
     *
     * @param thickness non-negative thickness value in pixels.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given value is negative, or, progress bar thickness is not supported by the current used chart mode.
     */
    public PercentageChartView progressBarThickness(float thickness) {
        if (thickness < 0) {
            throw new IllegalArgumentException("Progress bar thickness must be a positive value.");
        }

        try {
            ((RingModeRenderer) renderer).setProgressBarThickness(thickness);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Progress bar thickness is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Sets the progress bar stroke style. Works only if chart mode is set to ring.
     *
     * @param style Progress bar stroke style as a ProgressStyle constant.
     * @return the percentage chart view
     * @throws IllegalArgumentException if the given progress bar style is not a valid ProgressBarStyle constant, or, not supported by the current used chart mode.
     */
    public PercentageChartView progressBarStyle(@ProgressBarStyle int style) {
        if (style < 0 || style > 1) {
            throw new IllegalArgumentException("Progress bar style must be a valid TextStyle constant.");
        }

        try {
            ((RingModeRenderer) renderer).setProgressBarStyle(style);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Progress bar style is not support by the used percentage chart mode.");
        }
        return this;
    }

    /**
     * Apply all the requested changes.
     */
    public void apply() {
        postInvalidate();
    }

    /**
     * Set adaptive color provider *
     *
     * @param adaptiveColorProvider adaptive color provider
     */
    public void setAdaptiveColorProvider(AdaptiveColorProvider adaptiveColorProvider) {
        this.renderer.setAdaptiveColorProvider(adaptiveColorProvider);
    }

    /**
     * Set text formatter *
     *
     * @param textFormatter text formatter
     */
    public void setTextFormatter(ProgressTextFormatter textFormatter) {
        this.renderer.setTextFormatter(textFormatter);
    }

    /**
     * Set on progress change listener *
     *
     * @param onProgressChangeListener on progress change listener
     */
    public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
        this.onProgressChangeListener = onProgressChangeListener;
    }

}
